#pragma once

#include <AST/type/types.hpp>
#include <AST/scope/scope.hpp>
#include <llvm/IR/Value.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Instruction.h>
#include <llvm/Support/raw_ostream.h>
#include <graphviz.hpp>
class Expr : public ASTAcceptor {
  protected:
    using Value = llvm::Value;
    using Constant = llvm::Constant;
    using Type = llvm::Type;
    using string = std::string;
    using Function = llvm::Function;
    using FunctionType = llvm::FunctionType;
    using BasicBlock = llvm::BasicBlock;
    using ExprVector = std::vector<Expr*>;
    using PointerType = llvm::PointerType;
    using UndefValue = llvm::UndefValue;

  public:
    virtual Value *codegen() = 0;
    virtual ~Expr() = default;

    Value *codegen_print() {
        Value *ret = codegen();
        ret->print(llvm::errs(), true);
        return ret;
    }

    static Expr *sequenceOf(ExprVector& exprs);
    static Expr *sequenceOf(ExprVector&& exprs);
};

#ifndef NDEBUG
#include <iostream>
#endif

inline llvm::Value *logError(const std::string &what) {
#ifndef NDEBUG
    std::cerr << "[Error] " << what << std::endl;
#endif
    return nullptr;
}

inline llvm::Value *logWarning(const std::string &what) {
#ifndef NDEBUG
    std::cerr << "[Warning] " << what << std::endl;
#endif
    return nullptr;
}